﻿using NModbus;
using NModbus.Data;
using System.Net;
using System.Net.Sockets;


namespace modbus
{
    public partial class Form2 : Form
    {
        private ModbusTcpSlave modbusTcpSlave;

        public Form2()
        {
            InitializeComponent();
        }

        private async void button1_Click(object sender, EventArgs e)
        {
            byte unitId = 1; // 可根据实际需要设置
            modbusTcpSlave = new ModbusTcpSlave(unitId);

            // 监听本地所有IP，端口502
            modbusTcpSlave.Start(System.Net.IPAddress.Any);

            textBoxLog.AppendText("ModbusTcpSlave 已启动，等待主站连接...\r\n");
        }

        private void button2_Click(object sender, EventArgs e)
        {
            modbusTcpSlave?.Stop();
            textBoxLog.AppendText("ModbusTcpSlave 已停止。\r\n");
        }
    }

    /// <summary>
    /// TCP 从站实现
    /// </summary>
    public class ModbusTcpSlave
    {
        private TcpListener _tcpListener;
        private byte _unitId; // 从站地址（Unit ID）
        private Dictionary<ushort, ushort> _holdingRegisters; // 模拟保持寄存器（地址从 0 开始）

        public ModbusTcpSlave(byte unitId)
        {
            _unitId = unitId;
            _holdingRegisters = new Dictionary<ushort, ushort>();
            // 初始化寄存器（示例：地址 0-5 的值为 100-105）
            for (ushort i = 0; i <= 5; i++)
            {
                _holdingRegisters[i] = (ushort)(100 + i);
            }
        }

        /// <summary>
        /// 启动从站，监听 502 端口
        /// </summary>
        /// <param name="ipAddress">监听的 IP 地址（如 IPAddress.Any 表示所有网卡）</param>
        public void Start(IPAddress ipAddress = null)
        {
            ipAddress ??= IPAddress.Any;
            _tcpListener = new TcpListener(ipAddress, 502);
            _tcpListener.Start();
            Console.WriteLine($"Modbus TCP 从站启动，监听 {ipAddress}:502，从站地址：{_unitId}");
            
            // 异步接受主站连接
            _tcpListener.BeginAcceptTcpClient(AcceptTcpClientCallback, null);
        }

        /// <summary>
        /// 停止从站，关闭监听
        /// </summary>
        public void Stop()
        {
            _tcpListener?.Stop();
            _tcpListener?.Dispose();
            Console.WriteLine("Modbus TCP 从站已停止。");
        }

        /// <summary>
        /// 异步处理主站连接
        /// </summary>
        private void AcceptTcpClientCallback(IAsyncResult ar)
        {
            if (_tcpListener == null) return;

            TcpClient client = _tcpListener.EndAcceptTcpClient(ar);
            Console.WriteLine("主站连接成功，开始处理请求...");

            // 异步读取请求数据
            NetworkStream stream = client.GetStream();
            byte[] buffer = new byte[4096]; // 缓冲区（根据实际需求调整大小）
            stream.BeginRead(buffer, 0, buffer.Length, ReadRequestCallback, (stream, buffer));
        }

        /// <summary>
        /// 处理主站请求数据
        /// </summary>
        private void ReadRequestCallback(IAsyncResult ar)
        {
            if (_tcpListener == null) return;

            (NetworkStream stream, byte[] buffer) = ((NetworkStream, byte[]))ar.AsyncState;
            int bytesRead = stream.EndRead(ar);

            if (bytesRead > 0)
            {
                // 解析 Modbus TCP 报文（MBAP 头部 + 协议数据单元）
                (byte[] response, int responseLength) = ProcessRequest(buffer, bytesRead, stream);
                if (response != null && responseLength > 0)
                {
                    stream.Write(response, 0, responseLength);
                }
            }

            // 继续监听下一个请求（保持长连接）
            stream.BeginRead(buffer, 0, buffer.Length, ReadRequestCallback, (stream, buffer));
        }

        /// <summary>
        /// 处理主站请求，生成响应
        /// </summary>
        private (byte[] response, int responseLength) ProcessRequest(byte[] buffer, int bytesRead, NetworkStream stream)
        {
            // 解析 MBAP 头部（最小长度：7 字节）
            if (bytesRead < 7) return (null, 0);

            int transactionId = BitConverter.ToInt16(buffer, 0); // 事务标识符（2字节）
            int protocolId = BitConverter.ToInt16(buffer, 2);   // 协议标识符（2字节，Modbus 为 0）
            int length = BitConverter.ToInt16(buffer, 4);       // 后续数据长度（2字节，含 Unit ID 和 PDU）
            byte unitId = buffer[6];                          // 从站地址（Unit ID，1字节）
            byte[] pdu = new byte[length - 1];                // 协议数据单元（PDU，不含 Unit ID）
            Array.Copy(buffer, 7, pdu, 0, pdu.Length);

            // 验证从站地址（仅响应匹配的从站）
            if (unitId != _unitId) return (null, 0);

            // 解析功能码（PDU 第一个字节）
            byte functionCode = pdu[0];
            if (functionCode > 0x80) // 错误响应（功能码 >= 0x80）
            {
                return HandleErrorResponse(pdu, stream);
            }

            // 处理具体功能码（示例：读保持寄存器 0x03）
            switch (functionCode)
            {
                case 0x03: // 读保持寄存器
                    return HandleReadHoldingRegisters(pdu, transactionId);
                case 0x06: // 写单个寄存器
                    return HandleWriteSingleRegister(pdu, transactionId);
                default:
                    // 不支持的功能码，返回错误响应
                    return (EncodeErrorResponse(functionCode, 0x01), 9); // 错误码 0x01（非法功能）
            }
        }

        /// <summary>
        /// 处理读保持寄存器请求（功能码 0x03）
        /// </summary>
        private (byte[] response, int responseLength) HandleReadHoldingRegisters(byte[] pdu, int transactionId)
        {
            // 解析请求参数（起始地址、数量）
            if (pdu.Length < 5) return (null, 0); // 无效请求
            ushort startAddr = (ushort)(pdu[1] << 8 | pdu[2]); // 起始地址（2字节）
            ushort numRegisters = (ushort)(pdu[3] << 8 | pdu[4]); // 寄存器数量（2字节）

            // 验证寄存器地址范围（示例：仅支持 0-5）
            if (startAddr + numRegisters > 5) return (null, 0);

            // 读取寄存器数据（示例：返回预设值）
            List<byte> responseData = new List<byte>();
            responseData.Add(0x03); // 功能码（无错误）
            responseData.Add((byte)(numRegisters * 2)); // 数据长度（每个寄存器 2 字节）

            for (ushort i = 0; i < numRegisters; i++)
            {
                ushort regValue = _holdingRegisters[(ushort)(startAddr + i)];
                responseData.Add((byte)(regValue >> 8)); // 高字节
                responseData.Add((byte)regValue);      // 低字节
            }

            // 构造响应报文（MBAP 头部 + 响应数据）
            byte[] response = EncodeTcpResponse(transactionId, responseData.ToArray());
            return (response, response.Length);
        }

        /// <summary>
        /// 处理写单个寄存器请求（功能码 0x06）
        /// </summary>
        private (byte[] response, int responseLength) HandleWriteSingleRegister(byte[] pdu, int transactionId)
        {
            // 解析请求参数（寄存器地址、值）
            if (pdu.Length < 5) return (null, 0); // 无效请求
            ushort regAddr = (ushort)(pdu[1] << 8 | pdu[2]); // 寄存器地址（2字节）
            ushort regValue = (ushort)(pdu[3] << 8 | pdu[4]); // 寄存器值（2字节）

            // 验证寄存器地址范围（示例：仅支持 0-5）
            if (regAddr > 5) return (null, 0);

            // 更新寄存器值
            _holdingRegisters[regAddr] = regValue;

            // 构造响应报文（功能码 + 寄存器地址 + 寄存器值）
            List<byte> responseData = new List<byte>
        {
            0x06, // 功能码（无错误）
            (byte)(regAddr >> 8), (byte)regAddr, // 寄存器地址（2字节）
            (byte)(regValue >> 8), (byte)regValue // 寄存器值（2字节）
        };

            // 构造响应报文（MBAP 头部 + 响应数据）
            byte[] response = EncodeTcpResponse(transactionId, responseData.ToArray());
            return (response, response.Length);
        }

        /// <summary>
        /// 生成 Modbus TCP 响应报文（MBAP 头部 + 数据）
        /// </summary>
        private byte[] EncodeTcpResponse(int transactionId, byte[] data)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                // MBAP 头部：
                // - 事务标识符（2字节）：与请求一致
                // - 协议标识符（2字节）：0（Modbus）
                // - 长度（2字节）：数据长度 + 1（Unit ID） + 2（MBAP 头部长度）？不，MBAP 长度字段是“后续数据长度”（即 Unit ID + PDU 的长度）
                // - Unit ID（1字节）：从站地址
                ms.WriteByte((byte)(transactionId >> 8));
                ms.WriteByte((byte)transactionId);
                ms.WriteByte(0x00); // 协议标识符（Modbus 为 0）
                ms.WriteByte(0x00);
                // 长度字段：后续数据长度（Unit ID + PDU）= 1 + data.Length
                ms.WriteByte((byte)((data.Length + 1) >> 8));
                ms.WriteByte((byte)((data.Length + 1) & 0xFF));
                ms.WriteByte(_unitId); // Unit ID（从站地址）
                ms.Write(data, 0, data.Length);
                return ms.ToArray();
            }
        }

        /// <summary>
        /// 生成错误响应报文
        /// </summary>
        private byte[] EncodeErrorResponse(byte originalFunctionCode, byte errorCode)
        {
            // 错误响应格式：功能码（原功能码 + 0x80） + 错误码
            return new byte[] { (byte)(originalFunctionCode + 0x80), errorCode };
        }

        /// <summary>
        /// 处理错误响应（如非法功能码、非法数据地址）
        /// </summary>
        private (byte[] response, int responseLength) HandleErrorResponse(byte[] pdu, NetworkStream stream)
        {
            byte originalFunctionCode = (byte)(pdu[0] & 0x7F); // 提取原始功能码（去掉错误标志）
            byte errorCode = 0x01; // 默认错误码：非法功能（可根据实际情况调整）
            return (EncodeErrorResponse(originalFunctionCode, errorCode), 9); // 固定长度 9 字节
        }
    }
}